
import 'dart:io';

import 'package:path/path.dart';
import 'package:flutter/cupertino.dart';
import 'path_provider_ex.dart';

enum StorageDirectory {
  /// Contains audio files that should be treated as music.
  ///
  /// See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_MUSIC.
  music,

  /// Contains audio files that should be treated as podcasts.
  ///
  /// See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_PODCASTS.
  podcasts,

  /// Contains audio files that should be treated as ringtones.
  ///
  /// See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_RINGTONES.
  ringtones,

  /// Contains audio files that should be treated as alarm sounds.
  ///
  /// See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_ALARMS.
  alarms,

  /// Contains audio files that should be treated as notification sounds.
  ///
  /// See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_NOTIFICATIONS.
  notifications,

  /// Contains images. See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_PICTURES.
  pictures,

  /// Contains movies. See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_MOVIES.
  movies,

  /// Contains files of any type that have been downloaded by the user.
  ///
  /// See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_DOWNLOADS.
  downloads,

  /// Used to hold both pictures and videos when the device filesystem is
  /// treated like a camera's.
  ///
  /// See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_DCIM.
  dcim,

  /// Holds user-created documents. See https://developer.android.com/reference/android/os/Environment.html#DIRECTORY_DOCUMENTS.
  documents,
}

/// An exception thrown when a directory that should always be available on
/// the current platform cannot be obtained.
class MissingPlatformDirectoryException implements Exception {
  /// Creates a new exception
  MissingPlatformDirectoryException(this.message, {this.details});

  /// The explanation of the exception.
  final String message;

  /// Added details, if any.
  ///
  /// E.g., an error object from the platform implementation.
  final Object? details;

  @override
  String toString() {
    final String detailsAddition = details == null ? '' : ': $details';
    return 'MissingPlatformDirectoryException($message)$detailsAddition';
  }
}

PathProviderEx get _platform => PathProviderEx();

/// Path to the temporary directory on the device that is not backed up and is
/// suitable for storing caches of downloaded files.
///
/// Files in this directory may be cleared at any time. This does *not* return
/// a new temporary directory. Instead, the caller is responsible for creating
/// (and cleaning up) files or directories within this directory. This
/// directory is scoped to the calling application.
///
/// On iOS, this uses the `NSCachesDirectory` API.
///
/// On Android, this uses the `getCacheDir` API on the context.
///
/// Throws a `MissingPlatformDirectoryException` if the system is unable to
/// provide the directory.
Future<Directory> getTemporaryDirectory() async {
  return Directory(await getTemporaryPath());
}

///See [getTemporaryDirectory]
Future<String> getTemporaryPath() async {
  final String? path = await _platform.getTemporaryPath();
  if (path == null) {
    throw MissingPlatformDirectoryException(
        'Unable to get temporary directory');
  }
  return path;
}

/// Path to a directory where the application may place application support
/// files.
///
/// Use this for files you don’t want exposed to the user. Your app should not
/// use this directory for user data files.
///
/// On iOS, this uses the `NSApplicationSupportDirectory` API.
/// If this directory does not exist, it is created automatically.
///
/// On Android, this function uses the `getFilesDir` API on the context.
///
/// Throws a `MissingPlatformDirectoryException` if the system is unable to
/// provide the directory.
Future<Directory> getApplicationSupportDirectory() async {
  return Directory(await getApplicationSupportPath());
}

///See [getApplicationSupportDirectory]
Future<String> getApplicationSupportPath() async {
  final String? path = await _platform.getApplicationSupportPath();
  if (path == null) {
    throw MissingPlatformDirectoryException(
        'Unable to get application support directory');
  }

  return (path);
}

/// Path to the directory where application can store files that are persistent,
/// backed up, and not visible to the user, such as sqlite.db.
///
/// On Android, this function return database file, such as sqlite.db
///
/// Throws a `MissingPlatformDirectoryException` if the system is unable to
/// provide the directory on a supported platform.
Future<Directory> getLibraryDirectory(String name) async {
  return Directory(await getLibraryPath(name));
}

///See [getLibraryDirectory]
Future<String> getLibraryPath(String name) async {
  final String? path = await _platform.getLibraryPath(name);
  if (path == null) {
    throw MissingPlatformDirectoryException('Unable to get library directory');
  }
  if(Platform.isIOS) {
    return join(path, name);
  }
  return (path);
}

/// Path to a directory where the application may place data that is
/// user-generated, or that cannot otherwise be recreated by your application.
///
/// On iOS, this uses the `NSDocumentDirectory` API. Consider using
/// [getApplicationSupportDirectory] instead if the data is not user-generated.
///
/// On Android, this uses the `getDataDirectory` API on the context. Consider
/// using [getExternalStorageDirectory] instead if data is intended to be visible
/// to the user.
///
/// Throws a `MissingPlatformDirectoryException` if the system is unable to
/// provide the directory.
Future<Directory> getApplicationDocumentsDirectory() {
  return getApplicationDocumentsPath().then((value) => Directory(value));
}

///See [getApplicationDocumentsDirectory]
Future<String> getApplicationDocumentsPath() async {
  final String? path = await _platform.getApplicationDocumentsPath();
  if (path == null) {
    throw MissingPlatformDirectoryException(
        'Unable to get application documents directory');
  }
  return (path);
}

/// Path to a directory where the application may access top level storage.
/// The current operating system should be determined before issuing this
/// function call, as this functionality is only available on Android.
///
/// On iOS, this function throws an [UnsupportedError] as it is not possible
/// to access outside the app's sandbox.
///
/// On Android this uses the `getExternalFilesDir(null)`.
Future<Directory?> getExternalStorageDirectory() async {
  String? path = await getExternalStoragePath();
  if(path == null) {
    return null;
  }
  return Directory(path);
}

///See [getExternalStorageDirectory]
Future<String?> getExternalStoragePath() => _platform.getExternalStoragePath();


/// Paths to directories where application specific external cache data can be
/// stored. These paths typically reside on external storage like separate
/// partitions or SD cards. Phones may have multiple storage directories
/// available.
///
/// The current operating system should be determined before issuing this
/// function call, as this functionality is only available on Android.
///
/// On iOS, this function throws an UnsupportedError as it is not possible
/// to access outside the app's sandbox.
///
/// On Android this returns Context.getExternalCacheDirs() or
/// Context.getExternalCacheDir() on API levels below 19.
Future<List<Directory>?> getExternalCacheDirectories() async {
  final List<String>? paths = await getExternalCachePaths();
  if (paths == null) {
    return null;
  }
  return paths.map((String path) => Directory(path)).toList();
}

///See [getExternalCacheDirectories]
Future<List<String>?> getExternalCachePaths() => _platform.getExternalCachePaths();


/// Paths to directories where application specific data can be stored.
/// These paths typically reside on external storage like separate partitions
/// or SD cards. Phones may have multiple storage directories available.
///
/// The current operating system should be determined before issuing this
/// function call, as this functionality is only available on Android.
///
/// On iOS, this function throws an UnsupportedError as it is not possible
/// to access outside the app's sandbox.
///
/// On Android this returns Context.getExternalFilesDirs(String type) or
/// Context.getExternalFilesDir(String type) on API levels below 19.
///
/// Optional parameter. See [StorageDirectory] for more informations on
//  how this type translates to Android storage directories.
Future<List<Directory>?> getExternalStorageDirectories({StorageDirectory? type}) async {
  final List<String>? paths = await getExternalStoragePaths(type: type);
  if (paths == null) {
    return null;
  }
  return paths.map((String path) => Directory(path)).toList();
}

/// Optional parameter. See [StorageDirectory] for more informations on
/// how this type translates to Android storage directories.
Future<List<String>?> getExternalStoragePaths({StorageDirectory? type}) {
  return _platform.getExternalStoragePaths(type: type);
}

/// Path to the directory where downloaded files can be stored.
Future<Directory?> getDownloadsDirectory() async {
  final String? path = await getDownloadsPath();
  if (path == null) {
    return null;
  }
  return Directory(path);
}

Future<String?> getDownloadsPath() => _platform.getDownloadsPath();

///sdcard根路径
///On ios, this function throws an UnsupportedError
Future<String?> getExternalSdcardPath() => _platform.getExternalSdcardPath();

///获取视频文件的播放时长（单位：毫秒）
Future<int> getVideoDuration(String videoPath) => _platform.getVideoDuration(videoPath);

///获取存储卡里剩余可用容量(单位：byte)
Future<int> getAvailableSize() => _platform.getAvailableSize();

///获取剩余可用空间占总空间的百分比(0 ~ 100)
Future<int> getAvailableStoragePercent() => _platform.getAvailableStoragePercent();

installApk(String path) => _platform.installApk(path);

goAppleStore(String iosAppStoreId) => _platform.goAppleStore(iosAppStoreId);

///app允许环境监测
///
///返回值： 0 正常； 1  模拟器；2  ROOT；3  XPOSED；4 adb enable；5 开发者模式
Future<int> environmentCheck() => _platform.environmentCheck();

openAdbUI() => _platform.openAdbUI();

///获取原生项目里的图片信息
///
/// [imageType] use 'mipmap' or 'drawable'
Future<String?> nativeImageCachePath(String imageName, [String imageType = "mipmap", bool useCache = true]) =>
    _platform.nativeImageCachePath(imageName, imageType, useCache);

Widget loadNativeImage(String imageName, {String imageType = "mipmap", double scale = 1, bool useCache = true}) {
  return FutureBuilder<String?>(builder: (context, v) {
    if(v.connectionState == ConnectionState.done) {
      print("loadNativeImage= $v");
      if(v.data == null) return Container();
      return Image.file(File(v.data!), scale: scale);
    }
    return Container();
  }, future: nativeImageCachePath(imageName, imageType, useCache));
}

/// 从图片文件中读取经纬度、生成的时间、宽高等参数
///
///返回值 latitude longitude  time  width  height
Future<Map> getExifInfoFromMedia(String? path) {
  if(path == null || "".compareTo(path) == 0) return Future.value({});
  return _platform.getExifInfoFromMedia(path);
}

/// 生成缩略图
///
/// [maxWidth] 缩略图的宽度最大不会超过maxWidth
///
/// [maxHeight] 缩略图的高度最大不会超过maxHeight
Future<bool> thumbnailImage(String path, String thumbPath, int maxWidth, int maxHeight) async {
  return _platform.thumbnailImage(path, thumbPath, maxWidth, maxHeight);
}

/// 将图片展示在图库里(仅限android系统)
mediaScanner(String path) {
  if(Platform.isAndroid) {
    _platform.mediaScanner(path);
  }
}

/// 将文件移动到系统下载目录，原文件将删除(支持IOS和鸿蒙)
Future moveFile(String path) {
  if(Platform.isIOS) {
    File file = File(path);
    if(!file.existsSync()) return Future.value(false);
    return _platform.moveFile(path).then((value) {
      if(value == true) {
        file.deleteSync();
      }
      return value;
    });
  } else {
    return Future.value(false);
  }
}